#ifndef REGISTRATION_HPP
#define REGISTRATION_HPP

#include "AffineTransform.hpp"
#include <girlTools/girlToolsImage.hpp>

#include <vector>
#include <iostream>


/*!
  *
  * \brief This class can be used to align the verso and the recto when bleed through is present.
  *
  * \code
        Registration * blockRegistration = new BlockRegistration(recto,verso);
        Transformation * trans = blockRegistration->registerTransformation();

        std::cout << "BlockRegistration took : " << timer.elapsed() << std::endl;
        std::cout << "Transformation : " <<
                    "angle : " << trans->getParam().angle << std::endl <<
                    "dx : " << trans->getParam().dx << std::endl <<
                    "dy : " << trans->getParam().dy << std::endl;

        girl::tools::Image transformed = trans->transformed(verso);
  * \endcode
  *
  */
class Registration
{
public:
    Registration();
    Registration(const girl::tools::Image &image1, const girl::tools::Image &image2);

    virtual Transformation * registerTransformation();

    static double diff(const girl::tools::Image &image1,const girl::tools::Image &image2, int step = 1);

protected:

    /*!
      * This object is used to keep track of the diff score coresponding to the Transformation made with
      * the associated parameters.
      */
    struct DiffObj
    {
        DiffObj(double pDiff, const Transformation::TransformationParameter & pParams)
            : diff(pDiff), params(pParams){}

        void saveTransformationParameters(const Transformation::TransformationParameter &pParams)
        {params = pParams;}

        Transformation::TransformationParameter getTransformationParameters()
        {return params;}

        void saveDiff(double pDiff)
        {diff = pDiff;}

        double getDiff()
        {return diff;}

    protected :
        /*! Diff score */
        double diff;
        /*! parameters which coresponds to the diff score*/
        Transformation::TransformationParameter params;

    };

    /*! @deprecated */
    DiffObj getMinDiff(Transformation & t,
                        Transformation::TransformationParameter & params,
                        int range);

     girl::tools::Image _recto;
     girl::tools::Image _verso;
private :

    /*!
      * Each rotation creates a thread of ThreadTransformationAndDiff which will do the translations ...
      * @todo optimization + Memory leak (boost ?)
      */
    class ThreadTransformationAndDiff
    {
    public :
        Registration::DiffObj _finalMinDiff;
        girl::tools::Image _verso;
        const girl::tools::Image & _recto;
        Transformation::TransformationParameter _initialParameters;

        ThreadTransformationAndDiff(
                                    girl::tools::Image image,
                                    int diffOriginal,
                                    const girl::tools::Image &recto,
                                    Transformation::TransformationParameter initialParams);

        void run();
        Registration::DiffObj getDiff(){return _finalMinDiff;}
    };


};

/*!
  * \brief Registration by blocks
  *
  *  Each block represents a zone in the image. A thread registers each zone.
  *
  **/
class BlockRegistration : public Registration
{
public :

    BlockRegistration(const girl::tools::Image &image1, const girl::tools::Image &image2);

    /*! @overload */
    virtual Transformation * registerTransformation();

protected:
    /*! Defines a block of two images (recto and verso) that needs to be both registered
      */
    struct block
    {
        girl::tools::Image recto;
        girl::tools::Image verso;
    };

    /*!
      * sets the block b with the coresponding pixels on the original image (verso and recto)
      */
    virtual void getPreparedBlock(const girl::tools::Image &image1,
                  const girl::tools::Image &image2,
                  int x1,int y1,
                  int x2,int y2,
                  block &b);
    /*!
      * Creates the blocks on whitch the registrations will be made
      * @todo the percentages and blocks positions are Hard Coded.
      */
    virtual void createBlocks(const girl::tools::Image &image1,
                      const girl::tools::Image &image2,
                      std::vector<block> &blocks);

protected:
    /*! the blocks created out of the images (verso and recto)*/
    std::vector<block> blocks;
    /*! Due to a little hack to factorize some code lines, _recto and _verso varies
      * and wee need to save a copie of the original verso for the given back transformation
      */
    const girl::tools::Image _originalVerso;
};


/*!
  * \brief The registration is done on the center of the image.
  *
  * \see CenteredBlockRegistration::REREMOVED_BORDERS_PERCENTAGE
  */
class CenteredBlockRegistration : public Registration
{
public :
        static const int REMOVED_BORDERS_PERCENTAGE;
        CenteredBlockRegistration(const girl::tools::Image &image1, const girl::tools::Image &image2);
};

/*!
  *
  * \brief Both verso and recto are downscaled before registration.
  */
class ResizedRegistration : public Registration
{
public :
        /*!
          * @param factor (downscale factor)
          */
        ResizedRegistration(const girl::tools::Image &image1 , const girl::tools::Image &image2, int factor);
};

#endif // REGISTRATION_HPP
